package net.ltgt.gwt.protobuf.client;

import com.google.gwt.core.client.JsonUtils;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Internal;

public final class JsonFormat {
  // Do not allow instantiation.
  private JsonFormat() {}
  
  static final int JSONTYPE_STRING  = 0;
  static final int JSONTYPE_NUMBER  = 1;
  static final int JSONTYPE_OBJECT  = 2;
  static final int JSONTYPE_BOOLEAN = 3;
  static final int JSONTYPE_LONG    = 4;
  
  static final int JSONTYPE_ARRAY   = 8;

  static final int TAG_TYPE_BITS = 4;
  static final double TAG_TYPE_MULT = 1 << TAG_TYPE_BITS;
  static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;

  /** Given a tag value, determines the JSON type (the lower 4 bits). */
  static int getTagJsonType(final double tag) {
    return (int)tag & TAG_TYPE_MASK;
  }

  /** Given a tag value, determines the field number (the upper 29 bits). */
  public static int getTagFieldNumber(final double tag) {
    return (int) (tag / TAG_TYPE_MULT);
  }

  /** Makes a tag value given a field number and JSON type. */
  static double makeTag(final int fieldNumber, final int jsonType) {
    return (fieldNumber * TAG_TYPE_MULT) + jsonType;
  }

  /** Makes a tag value given a field number and JSON type. */
  static double makeArrayTag(final int fieldNumber, final int jsonType) {
    return makeTag(fieldNumber, JSONTYPE_ARRAY | jsonType);
  }

  /**
   * Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}.  This is
   * only here to support the lite runtime and should not be used by users.
   */
  public enum JavaType {
    INT(0),
    LONG(0L),
    FLOAT(0F),
    DOUBLE(0D),
    BOOLEAN(false),
    STRING(""),
    BYTE_STRING(ByteString.EMPTY),
    ENUM(null),
    MESSAGE(null);

    JavaType(final Object defaultDefault) {
      this.defaultDefault = defaultDefault;
    }

    /**
     * The default default value for fields of this type, if it's a primitive
     * type.
     */
    Object getDefaultDefault() {
      return defaultDefault;
    }

    private final Object defaultDefault;
  }

  /**
   * Lite equivalent to {@link Descriptors.FieldDescriptor.Type}.  This is
   * only here to support the lite runtime and should not be used by users.
   */
  public enum FieldType {
    DOUBLE  (JavaType.DOUBLE     , JSONTYPE_NUMBER ),
    FLOAT   (JavaType.FLOAT      , JSONTYPE_NUMBER ),
    INT64   (JavaType.LONG       , JSONTYPE_LONG   ),
    UINT64  (JavaType.LONG       , JSONTYPE_LONG   ),
    INT32   (JavaType.INT        , JSONTYPE_NUMBER ),
    FIXED64 (JavaType.LONG       , JSONTYPE_LONG   ),
    FIXED32 (JavaType.INT        , JSONTYPE_NUMBER ),
    BOOL    (JavaType.BOOLEAN    , JSONTYPE_BOOLEAN),
    STRING  (JavaType.STRING     , JSONTYPE_STRING ),
    GROUP   (JavaType.MESSAGE    , JSONTYPE_OBJECT ),
    MESSAGE (JavaType.MESSAGE    , JSONTYPE_OBJECT ),
    BYTES   (JavaType.BYTE_STRING, JSONTYPE_STRING ),
    UINT32  (JavaType.INT        , JSONTYPE_NUMBER ),
    ENUM    (JavaType.ENUM       , JSONTYPE_NUMBER ),
    SFIXED32(JavaType.INT        , JSONTYPE_NUMBER ),
    SFIXED64(JavaType.LONG       , JSONTYPE_LONG   ),
    SINT32  (JavaType.INT        , JSONTYPE_NUMBER ),
    SINT64  (JavaType.LONG       , JSONTYPE_LONG   );

    FieldType(final JavaType javaType, final int wireType) {
      this.javaType = javaType;
      this.wireType = wireType;
    }

    private final JavaType javaType;
    private final int wireType;

    public JavaType getJavaType() { return javaType; }
    public int getWireType() { return wireType; }
  }

  public static String toJson(boolean value) {
    return Boolean.toString(value);
  }
  
  public static String toJson(ByteString value) {
    return toJson(value.toStringUtf8());
  }
  
  public static String toJson(String value) {
    return JsonUtils.escapeValue(value);
  }
  
  public static native String toJson(Number value) /*-{
    return "" + value;
  }-*/;
  
  public static String toJson(Internal.EnumLite value) {
    return toJson(value.getNumber());
  }
}
